package model.entity.brain;

import java.util.HashSet;

import main.Log;
import model.entity.BasicEntity;
import model.map.Direction;
import model.map.GameMap;
import model.map.HexLocationProjection;
import model.map.Location;
import model.map.World;
import utilities.*;

public class MisbehaveState extends PetState implements Archiveable {
	protected String name;
	// to hold the location of the closest entity or item to chase (only avatar) if nothing 
	// else is close
	private Location closestItemOrEnt;
	
	public MisbehaveState(){
		name = "Misbehave";
		// is either the closest item or entity but if none are close
		// it is the avatar
		closestItemOrEnt = null;
	}
	
	public String getName(){
		return name;
	}
	
	/**
	 * gets the nextdirection to move in for an misbehaving Pet
	 */
	public Direction getNextMove(BasicEntity npc, Location avatarLoc, GameMap gm){
		HexLocationProjection hlp = new HexLocationProjection();
		Location tempLoc;
		Direction bestDir = Direction.SW;
		Direction dir = Direction.SW;
		int minDist = 999;
		
		// when the map doesnt have something to chase have it chase the avatar
		if(!findClosestItemOrEntity(npc, avatarLoc, gm))
			closestItemOrEnt = World.getInstance().getPlayer().getLocation();
		
		/*
		Log.out(Log.MODEL, "I AM ATb:" + npc.getLocation());
		Log.out(Log.MODEL, "   moving towards locatin:" + closestItemOrEnt);
		Log.out(Log.MODEL, "   avatar is at   locatin:" + World.getInstance().getPlayer().getLocation());
		Log.out(Log.MODEL, "   enitity exists at14,14 is:" + World.getInstance().getCurrentMap().hasEntity(new Location(18,13)));
		*/
		
		// cycle through each direction
		for(int i=0; i < 10; i ++){
			// skip numbers taht dont correspnd to directions below 4,5,6,
			if( i == 4)
				i = 7;
			
			// convert the number chosen to a direction
			switch(i){
				case 1: dir = Direction.SW; break;
				case 2: dir = Direction.S; break;
				case 3: dir = Direction.SE; break;
				case 7: dir = Direction.NW; break;
				case 8: dir = Direction.N; break;
				case 9: dir = Direction.NE; break;
			}
			
			// find the next block in that direction
			
			/* code here for if npc isnt updating, this forces on update of the entitys location
			myNPC = World.getInstance().getCurrentMap().getLocforEntity(npc);  // need this method added 
			World.getInstance().getCurrentMap().getEntity(theLoc);
			*/
			
			tempLoc = hlp.projectLocation(npc.getLocation(), dir);
			
			// find distance from where I would like to be
			int dist = distanceCalculator(tempLoc, closestItemOrEnt);
			// only store direction with shortest dist but not colliding with avatar
			if(tempLoc != avatarLoc && dist < minDist){
				bestDir = dir;
				minDist = dist;
			}
		}
		return bestDir;
	}

	/**
	 * finds the closest Item or Entity (not including the Avatar) but if none
	 * are found then it is the avatar. sets closestItemOrEntity equal to that 
	 * things location
	 * @param npc
	 * @param avatarLoc
	 * @param gm
	 */
	private boolean findClosestItemOrEntity(BasicEntity npc, Location avatarLoc, GameMap gm){
		HexLocationProjection hlp = new HexLocationProjection();
		Direction dir = Direction.SW;
		Location tempLoc;
		closestItemOrEnt = null;
		
		// this is the surrounding locations
		HashSet <Location>locSet = new HashSet <Location>();
	
		// add the current location
		locSet.add(npc.getLocation());

		// look at all locations within a radius of 5
		for(int radius = 1; radius < 5; radius++){
			int size = locSet.size();
			Object[] arrlocatSet = locSet.toArray();
			for(int ct=0; ct < size; ct++){
				// check every direction
				for(int i=0; i<10; i++){
					// skip illegal directions 
					if(i == 4)
						i = 7;
					// convert the number chosen to a direction
					switch(i){ 
						case 1: dir = Direction.SW; break;
						case 2: dir = Direction.S; break;
						case 3: dir = Direction.SE; break;
						case 7: dir = Direction.NW; break;
						case 8: dir = Direction.N; break;
						case 9: dir = Direction.NE; break;
					}
					
					// currLoc is where we are
					// temploc is where we are looking
					Location currLoc = (Location) arrlocatSet[ct];
					tempLoc = hlp.projectLocation(currLoc, dir);
					// add each checked location to the set
					if( !locSet.contains(tempLoc) ){
						locSet.add(tempLoc);
						arrlocatSet = locSet.toArray();
					}
					if( tempLoc.equals(new Location(18,13))){
						Log.out(Log.MODEL, "we should see it nowwwwwwwwwwww");
					}
					// get the projection in this direction in a hex grid
					//Location theLoc = hlp.projectLocation(currLoc, dir);
					// reget avatar location
					avatarLoc = World.getInstance().getPlayer().getLocation();
					// if the avatar is in the influence radius we are building return true
					//if((gm.hasEntity(tempLoc) && tempLoc != avatarLoc) || gm.hasItem(tempLoc) ){
					if((World.getInstance().getCurrentMap().hasEntity(tempLoc) && !tempLoc.equals(avatarLoc) && !tempLoc.equals(npc.getLocation())) || World.getInstance().getCurrentMap().hasItem(tempLoc) ){	
						
						// keep from straying too far from avatar
						// could happen if it chases one item, then the next and so on
						// so the item must be close to the avatar, so the pet stays close
						
						if( distanceCalculator(tempLoc, avatarLoc) <=5 ){
							closestItemOrEnt = tempLoc;
							return true;
						}	
					}
				}
			}
			
		}
		return false;
	}

	/**
	 * calculates the distance between two locations
	 * @param loc1
	 * @param loc2
	 * @return
	 */
	private int distanceCalculator(Location loc1, Location loc2){
		int xdist = loc1.getX() - loc2.getX();
		int ydist = loc1.getY() - loc2.getY();
		// keep distances positive
		if(xdist < 0){
			xdist = -xdist;
		}
		if(ydist < 0){
			ydist = -ydist;
		}
		return xdist+ydist;
	}
	
	
	public Archive save() {
		Archive archive = ArchiveFactory.createArchive();
		
//		archive.putClassName("closestItemOrEnt", this.closestItemOrEnt.getClass().getName());
//		archive.putOneRelationship("closestItemOrEnt", this.closestItemOrEnt);
//		
//		archive.putAttribute("name", this.name);
		
		return archive;
	}
	
	public void load(Archive archive) {
//		this.closestItemOrEnt = (Location)ModelFactory.getInstance().createInstanceWithClassName("closestItemOrEnt");
//		this.closestItemOrEnt.load(archive.getOneRelationship("closestItemOrEnt"));
//		
//		this.name = archive.getAttribute("name");
	}
	
	
}
		